﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ICSharpCode.TextEditor.Document;
using ICSharpCode.TextEditor;
using System.IO;
using FR.CodeVisualizer.Helpers;
using FR.CodeVisualizer.Visualizers;
using OfficeOpenXml;
using System.Diagnostics;
using OfficeOpenXml.Table;

namespace FR.CodeVisualizer.Views
{
    public enum SupportedLanguage
    {
        Text,
        XML,
        HTML,
        SQL
    }

    public partial class FormMainTextEdit : Form
    {
        public FormMainTextEdit()
        {
            InitializeComponent();
        }

        private DataSet dsVisualizacao;
        private BindingSource bsVisualizacao;
        private bool showSqlPanel;

        #region public properties

        private string efConnectionString;
        private string connectionString;
        public string ConnectionString
        {
            get
            {
                connectionString = txtConnectionString.Text;
                return connectionString;
            }
            set
            {
                connectionString = value;
                txtConnectionString.Text = value;
                if (!string.IsNullOrEmpty(connectionString) && showSqlPanel)
                {
                    splitContainer.Panel2Collapsed = false;
                    runToolStripMenuItem.Visible = true;
                }
                else
                {
                    splitContainer.Panel2Collapsed = true;
                    runToolStripMenuItem.Visible = false;
                }
            }
        }

        public string FiltroNomesColunas
        {
            get { return txtFilterColums.Text; }
            set { txtFilterColums.Text = value; }
        }

        public string FiltroNomesColunasRemover
        {
            get { return txtRemoveColumn.Text; }
            set { txtRemoveColumn.Text = value; }
        }

        public string FiltroDataTableSelect
        {
            get { return txtTableSelect.Text; }
            set { txtTableSelect.Text = value; }
        }

        public string SourceCodeText
        {
            get { return ucTextEditor.Text; }
            private set { ucTextEditor.Text = value; }
        }

        #endregion public properties

        public void Init(String st, string text = "", string connectionString = "", bool editable = false, bool showSqlPanel = false)
        {
            if (!editable)
                saveAndExitToolStripMenuItem.Visible = false;
            this.showSqlPanel = showSqlPanel;

            if (!string.IsNullOrWhiteSpace(connectionString))
            {
                this.ConnectionString = connectionString;
                efConnectionString = connectionString;
            }

            if (!string.IsNullOrWhiteSpace(text))
                this.Text += text;

            if (!string.IsNullOrEmpty(st))
                ucTextEditor.Text = st;

            if (_editorSettings == null)
            {
                LoadEditorSettingsFormConfiguration();
                OnSettingsChanged(persisteChanges: false);
            }
            else
                ucTextEditor.TextEditorProperties = _editorSettings;

            splitContainer.Panel2Collapsed = true;
            runToolStripMenuItem.Visible = false;
        }

        public void SetLanguage(SupportedLanguage language)
        {
            switch (language)
            {
                case SupportedLanguage.XML:
                    SetHighlight("XML");
                    break;
                case SupportedLanguage.HTML:
                    SetHighlight("HTML");
                    break;
                case SupportedLanguage.SQL:
                    SetHighlight("SQL");
                    break;
                case SupportedLanguage.Text:
                default:
                    SetHighlight("Default");
                    break;
            }
        }

        public void SetExceptionMessage(string message)
        {
            dataGridViewResults.Visible = false;
            txtExceptionResults.Text = message;
        }

        /// This variable holds the settings (whether to show line numbers, etc.)
        ITextEditorProperties _editorSettings;
        FindAndReplaceForm _findForm = new FindAndReplaceForm();

        #region Menu Events

        #region File

        private void menuFileSaveAs_Click(object sender, EventArgs e)
        {
            DoSaveAs(ucTextEditor);
        }

        private void menuFileExit_Click(object sender, EventArgs e)
        {
            this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
            this.Close();
        }

        private void saveAndExitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.DialogResult = System.Windows.Forms.DialogResult.OK;
            this.Close();
        }
        #endregion File

        #region Edit

        private void menuEditCut_Click(object sender, EventArgs e)
        {
            if (HaveSelection(ucTextEditor))
                DoEditAction(ucTextEditor, new ICSharpCode.TextEditor.Actions.Cut());
        }

        private void menuEditCopy_Click(object sender, EventArgs e)
        {
            if (HaveSelection(ucTextEditor))
                DoEditAction(ucTextEditor, new ICSharpCode.TextEditor.Actions.Copy());
        }

        private void menuEditPaste_Click(object sender, EventArgs e)
        {
            if (HaveSelection(ucTextEditor))
                DoEditAction(ucTextEditor, new ICSharpCode.TextEditor.Actions.Paste());
        }

        private void menuEditDelete_Click(object sender, EventArgs e)
        {
            if (HaveSelection(ucTextEditor))
                DoEditAction(ucTextEditor, new ICSharpCode.TextEditor.Actions.Delete());
        }

        private void menuEditFind_Click(object sender, EventArgs e)
        {
            _findForm.ShowFor(ucTextEditor, false);
        }

        private void menuEditFindAndReplace_Click(object sender, EventArgs e)
        {
            _findForm.ShowFor(ucTextEditor, true);
        }

        private void menuEditFindAgain_Click(object sender, EventArgs e)
        {
            _findForm.FindNext(true, false,
                string.Format("Search text «{0}» not found.", _findForm.LookFor));
        }

        private void menuEditFindAgainreverse_Click(object sender, EventArgs e)
        {
            _findForm.FindNext(true, true,
                string.Format("Search text «{0}» not found.", _findForm.LookFor));
        }

        private void menuEditToggleBookmark_Click(object sender, EventArgs e)
        {
            DoEditAction(ucTextEditor, new ICSharpCode.TextEditor.Actions.ToggleBookmark());
            ucTextEditor.IsIconBarVisible = ucTextEditor.Document.BookmarkManager.Marks.Count > 0;
        }

        private void menuEditGoToNextBookmark_Click(object sender, EventArgs e)
        {
            DoEditAction(ucTextEditor, new ICSharpCode.TextEditor.Actions.GotoNextBookmark
                (bookmark => true));
        }

        private void menuEditGoToPreviousBookmark_Click(object sender, EventArgs e)
        {
            DoEditAction(ucTextEditor, new ICSharpCode.TextEditor.Actions.GotoPrevBookmark
                (bookmark => true));
        }

        #endregion Edit

        #region Options

        private void menuOptionsShowSpacesAndTabs_Click(object sender, EventArgs e)
        {
            ucTextEditor.ShowSpaces = ucTextEditor.ShowTabs = !ucTextEditor.ShowSpaces;
            OnSettingsChanged();
        }

        private void menuOptionsShowNewLines_Click(object sender, EventArgs e)
        {
            ucTextEditor.ShowEOLMarkers = !ucTextEditor.ShowEOLMarkers;
            OnSettingsChanged();
        }

        private void menuOptionsShowLineNumbers_Click(object sender, EventArgs e)
        {
            ucTextEditor.ShowLineNumbers = !ucTextEditor.ShowLineNumbers;
            OnSettingsChanged();
        }

        private void menuOptionsHighlightCurrentRow_Click(object sender, EventArgs e)
        {
            ucTextEditor.LineViewerStyle = ucTextEditor.LineViewerStyle == LineViewerStyle.None
                ? LineViewerStyle.FullRow : LineViewerStyle.None;
            OnSettingsChanged();
        }

        private void menuOptionsHighlightMatchingBracketsWhenCursorIsLeft_Click(object sender, EventArgs e)
        {
            ucTextEditor.BracketMatchingStyle = ucTextEditor.BracketMatchingStyle == BracketMatchingStyle.After
                ? BracketMatchingStyle.Before : BracketMatchingStyle.After;
            OnSettingsChanged();
        }

        private void menuOptionsAutoInsertCurlyBracket_Click(object sender, EventArgs e)
        {

        }

        private void menuOptionsCutCopyWholeLine_Click(object sender, EventArgs e)
        {

        }

        private void menuOptionsShowMatchingBracket_Click(object sender, EventArgs e)
        {

        }

        private void menuOptionsAllowCursorPastEndofline_Click(object sender, EventArgs e)
        {
            ucTextEditor.AllowCaretBeyondEOL = !ucTextEditor.AllowCaretBeyondEOL;
            OnSettingsChanged();
        }

        private void menuOptionsSetTabSize_Click(object sender, EventArgs e)
        {
            string result = InputBox.Show("Specify the desired tab width.", "Tab size", _editorSettings.TabIndent.ToString());
            int value;
            if (result != null && int.TryParse(result, out value) && value.IsInRange(1, 32))
                ucTextEditor.TabIndent = value;
        }

        private void menuOptionsSetFont_Click(object sender, EventArgs e)
        {
            fontDialog.Font = ucTextEditor.Font;
            if (fontDialog.ShowDialog(this) == DialogResult.OK)
            {
                ucTextEditor.Font = fontDialog.Font;
                OnSettingsChanged();
            }
        }

        #endregion Options

        private void xMLToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SetHighlight("XML");
        }

        private void hTMLToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SetHighlight("HTML");
        }

        private void sQLToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SetHighlight("SQL");
        }

        private void textToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SetHighlight("Default");
        }

        private void runToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            RunSql();
        }

        private void exportExcelToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ExportToExcel();
        }

        private void exportXMLToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ExportToXML();
        }

        private void toogleSearchPanelToolStripMenuItem_Click(object sender, EventArgs e)
        {
            splitContainer1.Panel1Collapsed = !splitContainer1.Panel1Collapsed;
        }

        private void toogleResultsPanelToolStripMenuItem_Click(object sender, EventArgs e)
        {
            splitContainer.Panel2Collapsed = !splitContainer.Panel2Collapsed;
        }

        private void txtFilterColums_TextChanged(object sender, EventArgs e)
        {
            ShowOnlyColumns();
        }

        private void txtRemoveColumn_TextChanged(object sender, EventArgs e)
        {
            HideColumns();
        }

        private void btTableSelect_Click(object sender, EventArgs e)
        {
            DataTableSelect();
        }

        private void dataGridViewResults_DataError(object sender, DataGridViewDataErrorEventArgs e)
        {

        }
        #endregion

        #region TextEditor events

        private void ucTextEditor_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
                e.Effect = DragDropEffects.Copy;
        }

        #endregion

        #region private methods

        private void ShowOnlyColumns()
        {
            if (dataGridViewResults != null && dataGridViewResults.Columns != null && dataGridViewResults.Columns.Count > 0)
            {
                //para podermos filtrar por mais do que uma coluna
                string[] filtros = FiltroNomesColunas.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);

                DataGridViewColumn c = null;
                for (int i = 0; i < dataGridViewResults.Columns.Count; i++)
                {
                    c = dataGridViewResults.Columns[i];
                    if (string.IsNullOrWhiteSpace(FiltroNomesColunas))
                        c.Visible = true;
                    else if (filtros.Any(t => c.Name.IndexOf(t, StringComparison.OrdinalIgnoreCase) >= 0))
                        c.Visible = true;
                    else
                        c.Visible = false;
                }
            }
        }

        private void HideColumns()
        {
            if (dataGridViewResults != null && dataGridViewResults.Columns != null && dataGridViewResults.Columns.Count > 0)
            {
                //para podermos filtrar por mais do que uma coluna
                string[] filtros = FiltroNomesColunasRemover.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);

                DataGridViewColumn c = null;
                for (int i = 0; i < dataGridViewResults.Columns.Count; i++)
                {
                    c = dataGridViewResults.Columns[i];
                    if (string.IsNullOrWhiteSpace(FiltroNomesColunasRemover))
                        c.Visible = true;
                    else if (filtros.Any(t => c.Name.IndexOf(t, StringComparison.OrdinalIgnoreCase) >= 0))
                        c.Visible = false;
                    else
                        c.Visible = true;
                }
            }
        }

        private void DataTableSelect()
        {
            if (this.dsVisualizacao != null && this.dsVisualizacao.Tables.Count > 0)
            {
                try
                {
                    this.bsVisualizacao.Filter = FiltroDataTableSelect;
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Binding error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        private void ExportToExcel()
        {
            if (this.dsVisualizacao != null && this.dsVisualizacao.Tables.Count > 0)
            {
                ExcelPackage pck = new ExcelPackage();

                foreach (DataTable table in this.dsVisualizacao.Tables)
                {
                    var wsEnum = pck.Workbook.Worksheets.Add(table.TableName);

                    //Load the collection starting from cell A1...
                    wsEnum.Cells["A1"].LoadFromDataTable(table, true, TableStyles.Medium9);
                    wsEnum.Cells[wsEnum.Dimension.Address].AutoFitColumns();
                }

                //...and save
                string fileName = SaveFileDialog("Excel file|*.xlsx");
                if (!string.IsNullOrEmpty(fileName))
                {
                    var fi = new FileInfo(fileName);

                    pck.SaveAs(fi);

                    try
                    {
                        Process.Start(fi.FullName);
                    }
                    catch { }
                }
            }

        }

        private void ExportToXML()
        {
            string fileName = SaveFileDialog("XML file|*.xml");
            if (!string.IsNullOrEmpty(fileName))
            {
                if (this.dsVisualizacao != null && this.dsVisualizacao.Tables.Count > 0)
                    this.dsVisualizacao.Tables[0].WriteXml(fileName);
            }
        }

        private string SaveFileDialog(string fileType)
        {
            string nomeFicheiro = null;

            SaveFileDialog sfd = new SaveFileDialog();
            sfd.AddExtension = true;
            sfd.Filter = fileType;
            if (sfd.ShowDialog() == DialogResult.OK)
            {
                nomeFicheiro = sfd.FileName;
            }

            return nomeFicheiro;
        }

        private void SaveEditorSettings()
        {
            FR.CodeVisualizer.Properties.Settings.Default.ShowSpaces = _editorSettings.ShowSpaces;
            FR.CodeVisualizer.Properties.Settings.Default.ShowEOLMarker = _editorSettings.ShowEOLMarker;
            FR.CodeVisualizer.Properties.Settings.Default.ShowLineNumbers = _editorSettings.ShowLineNumbers;
            FR.CodeVisualizer.Properties.Settings.Default.AutoInsertCurlyBracket = _editorSettings.AutoInsertCurlyBracket;
            FR.CodeVisualizer.Properties.Settings.Default.CutCopyWholeLine = _editorSettings.CutCopyWholeLine;
            FR.CodeVisualizer.Properties.Settings.Default.ShowMatchingBracket = _editorSettings.ShowMatchingBracket;
            FR.CodeVisualizer.Properties.Settings.Default.AllowCaretBeyondEOL = _editorSettings.AllowCaretBeyondEOL;
            FR.CodeVisualizer.Properties.Settings.Default.HighlightCurrentRow = _editorSettings.LineViewerStyle == LineViewerStyle.FullRow;
            FR.CodeVisualizer.Properties.Settings.Default.HighlightMatching = _editorSettings.BracketMatchingStyle == BracketMatchingStyle.After;
        }

        private void LoadEditorSettingsFormConfiguration()
        {
            if (_editorSettings == null)
                _editorSettings = ucTextEditor.TextEditorProperties;

            _editorSettings.ShowSpaces = FR.CodeVisualizer.Properties.Settings.Default.ShowSpaces;
            _editorSettings.ShowEOLMarker = FR.CodeVisualizer.Properties.Settings.Default.ShowEOLMarker;
            _editorSettings.ShowLineNumbers = FR.CodeVisualizer.Properties.Settings.Default.ShowLineNumbers;
            _editorSettings.AutoInsertCurlyBracket = FR.CodeVisualizer.Properties.Settings.Default.AutoInsertCurlyBracket;
            _editorSettings.CutCopyWholeLine = FR.CodeVisualizer.Properties.Settings.Default.CutCopyWholeLine;
            _editorSettings.ShowMatchingBracket = FR.CodeVisualizer.Properties.Settings.Default.ShowMatchingBracket;
            _editorSettings.AllowCaretBeyondEOL = FR.CodeVisualizer.Properties.Settings.Default.AllowCaretBeyondEOL;
            if (FR.CodeVisualizer.Properties.Settings.Default.HighlightCurrentRow)
                _editorSettings.LineViewerStyle = LineViewerStyle.FullRow;
            if (FR.CodeVisualizer.Properties.Settings.Default.HighlightMatching)
                _editorSettings.BracketMatchingStyle = BracketMatchingStyle.After;
        }

        bool _highlightingProviderLoaded = false;
        private void SetHighlight(string language)
        {
            if (language == "SQL")
            {
                if (!_highlightingProviderLoaded)
                {
                    // Attach to the text editor.
                    HighlightingManager.Manager.AddSyntaxModeFileProvider(
                        new AppSyntaxModeProvider());
                    _highlightingProviderLoaded = true;
                }

                ucTextEditor.SetHighlighting("SQL");

                if (string.IsNullOrEmpty(efConnectionString))
                    ConnectionString = @"Data Source=.\sqlexpress;Initial Catalog=;User ID=sa;Password=;";
                else
                    ConnectionString = efConnectionString;
            }
            else
            {
                ucTextEditor.SetHighlighting(language);
                ConnectionString = "";
            }
        }

        private void RunSql()
        {
            dataGridViewResults.Visible = true;
            this.dsVisualizacao = null;
            this.bsVisualizacao = null;
            if (!string.IsNullOrEmpty(this.ConnectionString))
            {
                try
                {
                    string sql = string.IsNullOrEmpty(ucTextEditor.ActiveTextAreaControl.SelectionManager.SelectedText) ? ucTextEditor.Text : ucTextEditor.ActiveTextAreaControl.SelectionManager.SelectedText;

                    this.dsVisualizacao = ExecuteSQL.DevolveDataSetPorSql(sql, this.ConnectionString);
                    if (this.dsVisualizacao != null && this.dsVisualizacao.Tables.Count > 0)
                    {
                        this.bsVisualizacao = new BindingSource();
                        this.bsVisualizacao.DataSource = this.dsVisualizacao;
                        this.bsVisualizacao.DataMember = this.dsVisualizacao.Tables[0].TableName;

                        dataGridViewResults.DataSource = this.bsVisualizacao;
                    }
                }
                catch (Exception ex)
                {
                    SetExceptionMessage(ex.Message);
                }
            }
        }

        private bool DoSaveAs(TextEditorControl editor)
        {
            saveFileDialog.FileName = editor.FileName;
            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    editor.SaveFile(saveFileDialog.FileName);
                    editor.Parent.Text = Path.GetFileName(editor.FileName);
                    SetModifiedFlag(editor, false);

                    // The syntax highlighting strategy doesn't change automatically.
                    editor.Document.HighlightingStrategy =
                        HighlightingStrategyFactory.CreateHighlightingStrategyForFile(editor.FileName);
                    return true;
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, ex.GetType().Name);
                }
            }
            return false;
        }

        /// <summary>Gets whether the file in the specified editor is modified.</summary>
        /// <remarks>TextEditorControl doesn't maintain its own internal modified 
        /// flag, so we use the '*' shown after the file name to represent the 
        /// modified state.</remarks>
        private bool IsModified(TextEditorControl editor)
        {
            // TextEditorControl doesn't seem to contain its own 'modified' flag, so 
            // instead we'll treat the "*" on the filename as the modified flag.
            return editor.Parent.Text.EndsWith("*");
        }

        private void SetModifiedFlag(TextEditorControl editor, bool flag)
        {
            if (IsModified(editor) != flag)
            {
                var p = editor.Parent;
                if (IsModified(editor))
                    p.Text = p.Text.Substring(0, p.Text.Length - 2);
                else
                    p.Text += " *";
            }
        }

        /// <summary>Performs an action encapsulated in IEditAction.</summary>
        /// <remarks>
        /// There is an implementation of IEditAction for every action that 
        /// the user can invoke using a shortcut key (arrow keys, Ctrl+X, etc.)
        /// The editor control doesn't provide a public function to perform one
        /// of these actions directly, so I wrote DoEditAction() based on the
        /// code in TextArea.ExecuteDialogKey(). You can call ExecuteDialogKey
        /// directly, but it is more fragile because it takes a Keys value (e.g.
        /// Keys.Left) instead of the action to perform.
        /// <para/>
        /// Clipboard commands could also be done by calling methods in
        /// editor.ActiveTextAreaControl.TextArea.ClipboardHandler.
        /// </remarks>
        private void DoEditAction(TextEditorControl editor, ICSharpCode.TextEditor.Actions.IEditAction action)
        {
            if (editor != null && action != null)
            {
                var area = editor.ActiveTextAreaControl.TextArea;
                editor.BeginUpdate();
                try
                {
                    lock (editor.Document)
                    {
                        action.Execute(area);
                        if (area.SelectionManager.HasSomethingSelected && area.AutoClearSelection)
                        {
                            if (area.Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal)
                                area.SelectionManager.ClearSelection();
                        }
                    }
                }
                finally
                {
                    editor.EndUpdate();
                    area.Caret.UpdateCaretPosition();
                }
            }
        }

        private bool HaveSelection(TextEditorControl editor)
        {
            return editor != null &&
                editor.ActiveTextAreaControl.TextArea.SelectionManager.HasSomethingSelected;
        }

        /// <summary>Show current settings on the Options menu</summary>
        /// <remarks>We don't have to sync settings between the editors because 
        /// they all share the same DefaultTextEditorProperties object.</remarks>
        private void OnSettingsChanged(bool persisteChanges = true)
        {
            menuOptionsShowSpacesAndTabs.Checked = _editorSettings.ShowSpaces;
            menuOptionsShowNewLines.Checked = _editorSettings.ShowEOLMarker;
            menuOptionsShowLineNumbers.Checked = _editorSettings.ShowLineNumbers;
            menuOptionsAllowCursorPastEndofline.Checked = _editorSettings.AllowCaretBeyondEOL;
            menuOptionsHighlightCurrentRow.Checked = _editorSettings.LineViewerStyle == LineViewerStyle.FullRow;
            menuOptionsHighlightMatchingBracketsWhenCursorIsLeft.Checked = _editorSettings.BracketMatchingStyle == BracketMatchingStyle.After;

            //save the settings configuration
            if (persisteChanges)
                SaveEditorSettings();
        }

        #endregion private methods

        private void aboutToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            About a = new About();
            a.ShowDialog();
        }
    }
}
